﻿using Fast.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Fast.Swagger
{
    /// <summary>
    /// swagger文档构建器
    /// </summary>
    [SkipScan]
    public class SwaggerDocumentBuilder
    {
        /// <summary>
        /// Swagger文档配置
        /// </summary>
        private static readonly SwaggerDocumentSettingsOptions _swaggerDocumentSettings;
        /// <summary>
        /// 构造函数
        /// </summary>
        static SwaggerDocumentBuilder()
        {
            _swaggerDocumentSettings = App.ServicesProvider.GetService<IOptions<SwaggerDocumentSettingsOptions>>()?.Value;
        }

        /// <summary>
        /// Swagger 生成器构建
        /// </summary>
        /// <param name="swaggerGenOptions">Swagger 生成器配置</param>
        /// <param name="configure">自定义配置</param>
        internal static void BuildGen(SwaggerGenOptions swaggerGenOptions, Action<SwaggerGenOptions> configure = null)
        {
            // 创建分组文档
            ConfigureSwaggerDocs(swaggerGenOptions);

            // 加载分组控制器和动作方法列表
            LoadGroupControllerWithActions(swaggerGenOptions);

            // 配置 Swagger SchemaId
            ConfigureSchemaId(swaggerGenOptions);

            // 加载注释描述文件
            LoadXmlComments(swaggerGenOptions);

            // 配置授权
            ConfigureSecurities(swaggerGenOptions);

            //使得 Swagger 能够正确地显示 Enum 的对应关系
            swaggerGenOptions.SchemaFilter<EnumSchemaFilter>();

            // 自定义配置
            configure?.Invoke(swaggerGenOptions);
        }

        /// <summary>
        /// 构建Swagger全局配置
        /// </summary>
        /// <param name="swaggerOptions"></param>
        /// <param name="configure"></param>
        internal static void Build(SwaggerOptions swaggerOptions, Action<SwaggerOptions> configure = null)
        {
            //// 生成V2版本
            //swaggerOptions.SerializeAsV2 = _specificationDocumentSettings.FormatAsV2 == true;

            //// 判断是否启用 Server
            //if (_specificationDocumentSettings.HideServers != true)
            //{
            //    // 启动服务器 Servers
            //    swaggerOptions.PreSerializeFilters.Add((swagger, request) =>
            //    {
            //        // 默认 Server
            //        var servers = new List<OpenApiServer> {
            //            new OpenApiServer { Url = $"{request.Scheme}://{request.Host.Value}{_specificationDocumentSettings.VirtualPath}",Description="Default" }
            //        };
            //        servers.AddRange(_specificationDocumentSettings.Servers);

            //        swagger.Servers = servers;
            //    });
            //}

            // 配置路由模板
            swaggerOptions.RouteTemplate = _swaggerDocumentSettings.RouteTemplate;

            // 自定义配置
            configure?.Invoke(swaggerOptions);
        }

        /// <summary>
        /// Swagger UI 构建
        /// </summary>
        /// <param name="swaggerUIOptions"></param>
        /// <param name="routePrefix"></param>
        /// <param name="configure"></param>
        internal static void BuildUI(SwaggerUIOptions swaggerUIOptions, string routePrefix = default, Action<SwaggerUIOptions> configure = null)
        {
            // 配置分组终点路由
            ConfigureGroupEndpoint(swaggerUIOptions);

            // 配置文档标题
            swaggerUIOptions.DocumentTitle = _swaggerDocumentSettings.DocumentTitle;

            // 配置UI地址
            swaggerUIOptions.RoutePrefix = _swaggerDocumentSettings.RoutePrefix ?? routePrefix ?? "";

            //配置模型折叠
            swaggerUIOptions.DefaultModelsExpandDepth(_swaggerDocumentSettings.ModelsExpandDepth.Value);

            // 文档展开设置
            swaggerUIOptions.DocExpansion(_swaggerDocumentSettings.DocExpansionState.Value);

            // 自定义 Swagger 首页 (集成MiniProfiler)
            CustomSwaggerIndex(swaggerUIOptions);

            //// 配置多语言和自动登录token
            //AddDefaultInterceptor(swaggerUIOptions);

            // 自定义配置
            configure?.Invoke(swaggerUIOptions);
        }

        #region Privates
        /// <summary>
        /// 配置分组
        /// </summary>
        /// <param name="swaggerGenOptions"></param>
        private static void ConfigureSwaggerDocs(SwaggerGenOptions swaggerGenOptions)
        {
            foreach (var item in _swaggerDocumentSettings.SwaggerDocGroup)
            {
                if (!swaggerGenOptions.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(item.Key))
                {
                    swaggerGenOptions.SwaggerDoc(item.Key, item.Value);
                }
            }
        }

        /// <summary>
        /// 加载分组控制器和动作方法列表
        /// </summary>
        /// <param name="swaggerGenOptions">Swagger 生成器配置</param>
        private static void LoadGroupControllerWithActions(SwaggerGenOptions swaggerGenOptions)
        {
            // 为选择Action提供自定义策略。
            swaggerGenOptions.DocInclusionPredicate((docName, apiDescription) =>
            {
                if (!apiDescription.TryGetMethodInfo(out var method) || typeof(Controller).IsAssignableFrom(method.ReflectedType)) return false;

                var groups = method.DeclaringType.GetCustomAttributes<ApiExplorerSettingsAttribute>(true).Select(m => m.GroupName).ToList();
                if (groups.Count == 0)
                    groups.Add(SwaggerDoc.DefaultName);

                var actionGroups = method.GetCustomAttributes(true).OfType<ApiExplorerSettingsAttribute>().Select(m => m.GroupName);
                if (actionGroups.Any())
                    return actionGroups.Any(v => v == docName);

                return groups.Any(v => v == docName);
            });
        }

        /// <summary>
        /// 配置 Swagger SchemaId
        /// </summary>
        /// <param name="swaggerGenOptions">Swagger 生成器配置</param>
        private static void ConfigureSchemaId(SwaggerGenOptions swaggerGenOptions)
        {
            // 本地函数
            static string DefaultSchemaIdSelector(Type modelType)
            {
                if (!modelType.IsConstructedGenericType) return modelType.Name;

                var prefix = modelType.GetGenericArguments()
                    .Select(genericArg => DefaultSchemaIdSelector(genericArg))
                    .Aggregate((previous, current) => previous + current);

                // 通过 _ 拼接多个泛型
                return modelType.Name.Split('`').First() + "_" + prefix;
            }

            // 调用本地函数
            swaggerGenOptions.CustomSchemaIds(modelType => DefaultSchemaIdSelector(modelType));
        }

        /// <summary>
        /// 加载注释描述文件
        /// </summary>
        /// <param name="swaggerGenOptions">Swagger 生成器配置</param>
        private static void LoadXmlComments(SwaggerGenOptions swaggerGenOptions)
        {
            var xmlComments = _swaggerDocumentSettings.XmlComments;
            foreach (var xmlComment in xmlComments)
            {
                var assemblyXmlName = xmlComment.EndsWith(".xml") ? xmlComment : $"{xmlComment}.xml";
                var assemblyXmlPath = Path.Combine(AppContext.BaseDirectory, assemblyXmlName);
                if (File.Exists(assemblyXmlPath))
                {
                    swaggerGenOptions.IncludeXmlComments(assemblyXmlPath, true);
                }
            }
        }
        /// <summary>
        /// 配置授权
        /// </summary>
        /// <param name="swaggerGenOptions">Swagger 生成器配置</param>
        private static void ConfigureSecurities(SwaggerGenOptions swaggerGenOptions)
        {
            // 判断是否启用了授权
            if (_swaggerDocumentSettings.EnableAuthorized != true || _swaggerDocumentSettings.SecurityDefinition == null) return;

            var securityDefinition = _swaggerDocumentSettings.SecurityDefinition;
            if (!string.IsNullOrWhiteSpace(securityDefinition.Name))
            {
                if (!swaggerGenOptions.SwaggerGeneratorOptions.SecuritySchemes.ContainsKey(securityDefinition.Name))
                {
                    swaggerGenOptions.AddSecurityDefinition(securityDefinition.Name, securityDefinition.OpenApiSecurityScheme);
                }
            }

            var securityRequirement = _swaggerDocumentSettings.SecurityRequirement;
            if (securityRequirement != null && securityRequirement.OpenApiSecurityScheme != null && !string.IsNullOrWhiteSpace(securityRequirement.OpenApiSecurityScheme.Reference.Id))
            {
                swaggerGenOptions.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    { securityRequirement.OpenApiSecurityScheme, securityRequirement.Accesses??new string[]{ } }
                });
            }
        }

        /// <summary>
        /// 配置分组终点路由
        /// </summary>
        /// <param name="swaggerUIOptions"></param>
        private static void ConfigureGroupEndpoint(SwaggerUIOptions swaggerUIOptions)
        {
            foreach (var item in _swaggerDocumentSettings.SwaggerDocGroup)
            {
                // 替换路由模板
                var routeTemplate = _swaggerDocumentSettings.RouteTemplate.Replace("{documentName}", Uri.EscapeDataString(item.Key));
                swaggerUIOptions.SwaggerEndpoint($"/{routeTemplate}", item.Value.Title ?? item.Key);
            }
        }

        /// <summary>
        /// 自定义 Swagger 首页(集成MiniProfiler)
        /// </summary>
        /// <param name="swaggerUIOptions"></param>
        private static void CustomSwaggerIndex(SwaggerUIOptions swaggerUIOptions)
        {
            var thisType = typeof(SwaggerDocumentBuilder);
            var thisAssembly = thisType.Assembly;

            // 自定义 Swagger 首页
            var customIndex = $"{AssemblyHelper.GetAssemblyName(thisAssembly)}{thisType.Namespace.Replace(nameof(Fast), string.Empty)}.Assets.{(App.Settings.HasInjectMiniProfiler.Value ? "index-mini-profiler" : "index")}.html";
            swaggerUIOptions.IndexStream = () => thisAssembly.GetManifestResourceStream(customIndex);
        }
        #endregion
    }
}
